﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{
    /// <summary>
    /// A sparse array can be indexed by any number of indices (of type T1)
    /// Items in the array are of type T2
    /// </summary>
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class SparseArray<T1, T2> : MultiLevelDictionary<T1, T2> where T2 : class
    {
        public SparseArray() { }

        public bool IsFixedSize { get { return false; } }
        public bool IsReadOnly { get { return false; } }
        public bool IsSynchronized { get { return false; } }
        public int Count { get { return ItemCount; } }
        public int Rank { get { return MaxDepth(0); } }
        public object SyncRoot { get { return null; } }

        public void CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }

        public T1 GetLowerBound(int dimension)
        {
            return (from item in AllTerminatingItemsList where item.Key.Count > dimension select item.Key[dimension]).Min();
        }

        public T1 GetUpperBound(int dimension)
        {
            return (from item in AllTerminatingItemsList where item.Key.Count > dimension select item.Key[dimension]).Max();
        }

        public T2 GetValue(params T1[] indices) { return base.FindItem(indices); }
        public T2 GetValue(T1 index) { return GetValue(new T1[] { index }); }
        public T2 GetValue(T1 index1, T1 index2) { return GetValue(new T1[] { index1, index2 }); }

        public T2 GetValueOrCreate(Func<T2> create, params T1[] indices)
        {
            T2 item = GetValue(indices);

            if (item == null)
            {
                // RACE CONDITION?
                item = create();
                SetValue(item, indices);
            }

            return item;
        }

        public void SetValue(T2 value, params T1[] indices)
        {
            // RACE CONDITION?			
            if (value != null) { base.AddTerminatingItem((T2)value, indices); }
        }

        public void SetValue(T2 value, T1 index) { SetValue(value, new T1[] { index }); }
        public void SetValue(T2 value, T1 index1, T1 index2) { SetValue(value, new T1[] { index1, index2 }); }

        private class SparseArrayEnumerator : IEnumerator<T2>
        {
            IEnumerator _enum;
            SparseArray<T1, T2> _parent;
            List<KeyValuePair<List<T1>, T2>> _list;

            public void Dispose() { }

            public SparseArrayEnumerator(SparseArray<T1, T2> array)
            {
                _parent = array;
                _list = (from item in array.AllTerminatingItems select item).ToList();
                _enum = _list.GetEnumerator();
            }

            public void Reset() { _enum.Reset(); }
            public bool MoveNext() { return _enum.MoveNext(); }
            public object Current { get { return _enum.Current; } }
            T2 System.Collections.Generic.IEnumerator<T2>.Current { get { return (T2)_enum.Current; } }
            public int[] Index { get { return ((KeyValuePair<List<int>, T2>)Current).Key.ToArray(); } }
        }

        public System.Collections.IEnumerator GetEnumerator() { return new SparseArrayEnumerator(this); }
        public void RemoveAt(int index) { throw new NotImplementedException(); }
        public void Insert(int index, object value) { throw new NotImplementedException(); }
        public void Remove(T2 value) { }
        public bool Contains(object value) { return base.ContainsValue((T2)value); }

        public void Clear()
        {
            base.TerminatingItems.Clear();
            base.ItemLookup.Clear();
        }

        public int IndexOf(T2 value)
        {
            return 0;
        }

        public int Add(T2 value)
        {
            throw new NotImplementedException();
        }

        public T2 this[params T1[] indicies]
        {
            get { return GetValue(indicies); }
            set { SetValue(value, indicies); }
        }

        public T2 this[T1 index]
        {
            get { return GetValue(index); }
            set { SetValue(value, index); }
        }

        public T2 this[T1 index1, T1 index2]
        {
            get { return GetValue(index1, index2); }
            set { SetValue(value, index1, index2); }
        }

    }
}